--[[
	Library contains a few functions to help better utiltise the archaeology data
--]]
local MAJOR, MINOR = "LibArchaeology-0.1", tonumber("2") or 999
assert(LibStub, MAJOR.." requires LibStub")
local lib = LibStub:NewLibrary(MAJOR, MINOR)
if not lib then return end
lib.callbacks = lib.callbacks or LibStub("CallbackHandler-1.0"):New(lib)
local astrolabe = DongleStub("Astrolabe-1.0")

local L = LibArchLocals

--if not L then print("No locales found") end

local raceData = {}					-- race data cache
local luRaceName = {}				-- race name lookup table
local luCurrencyRace = {}			-- currency race lookup table
local luKeystoneRace = {}			-- keystone race lookup table
local luSiteIDName = {}				-- site blob id to name lookup table
local siteData = {}					-- all site data cache
local playerArtifactData = {}		-- player artifact data cache
local playerSiteData = {}			-- player site data cache
local mapcz = {}					-- map continent zone table
local currencyData = {}				-- currency data cache
local nodeData = { 					-- archaeology finds node table
		[L["Night Elf Archaeology Find"]]      = { id = 601, raceName = "Night Elf", raceID = 4, },
		[L["Troll Archaeology Find"]]          = { id = 602, raceName = "Troll", raceID = 8, },
		[L["Dwarf Archaeology Find"]]          = { id = 603, raceName = "Dwarf", raceID = 1, },
		[L["Fossil Archaeology Find"]]         = { id = 604, raceName = "Fossil", raceID = 3, },
		[L["Draenei Archaeology Find"]]        = { id = 605, raceName = "Draenei", raceID = 2, },
		[L["Orc Archaeology Find"]]            = { id = 606, raceName = "Orc", raceID = 6, },
		[L["Nerubian Archaeology Find"]]       = { id = 607, raceName = "Nerubian", raceID = 5, },
		[L["Vrykul Archaeology Find"]]         = { id = 608, raceName = "Vrykul", raceID = 9, },
		[L["Tol'vir Archaeology Find"]]        = { id = 609, raceName = "Tol'vir", raceID = 7, },
		[L["Other Archaeology Find"]]          = { id = 610, raceName = "Other", raceID = 10, },
}
local KEYSTONE_TO_FRAGMENTS = 12	-- 1 key stone = 12 fragments
local SITE_TEXTURE_INDEX = 177		-- shovel texture on the map
local CURRENCY_CATEGORY_ID = 82
local lastMap, lastFloor, lastBlob, lastSurveyPosition, lastSite, lastNearestSite
local artifactSolved, artifactChecked	-- artifact solving variables
local currRank, currMaxRank				-- current archaeology skill rank and max rank
local refreshLastRaceOnBagUpdate, bagRefreshWas, lastKeystoneAmount, lastRace			-- bag refresh vars
local tooltipLeftText1	= _G["GameTooltipTextLeft1"]
local prevSpell, curSpell, foundTarget
local archSpell = (GetSpellInfo(73979))	-- Searching for Artifacts spell
local AllowArtifactsUpdate = true
local counter, delay = 0, 4

-- cache some functions
local floor = floor
local GetProfessions, GetNumArchaeologyRaces = GetProfessions, GetNumArchaeologyRaces

do
	local function LoadMapCZTable()
		local orig = GetCurrentMapAreaID()
		for cid, zlist in pairs{GetMapContinents()} do
			SetMapZoom(cid)
			local mapid = GetCurrentMapAreaID()
			mapcz[mapid] = { cid, 0 }
			for zid, mapid in pairs{GetMapZones(cid)} do
				SetMapZoom(cid, zid)
				local mapid = GetCurrentMapAreaID()
				mapcz[mapid] = { cid, zid }
			end
		end
		SetMapByID(orig)
	end
	
	siteData = {
		[L["Ironband's Excavation Site"]] = { continent = 2, map = 35, blob = 54097, race = 1, points = {} }, 
		[L["Ironbeard's Tomb"]] = { continent = 2, map = 40, blob = 54124, race = 1, points = {} }, 
		[L["Whelgar's Excavation Site"]] = { continent = 2, map = 40, blob = 54126, race = 1, points = {} }, 
		[L["Greenwarden's Fossil Bank"]] = { continent = 2, map = 40, blob = 54127, race = 3, points = {} }, 
		[L["Thoradin's Wall"]] = { continent = 2, map = 16, blob = 54129, race = 1, points = {} }, 
		[L["Witherbark Digsite"]] = { continent = 2, map = 16, blob = 54132, race = 8, points = {} }, 
		[L["Thandol Span"]] = { continent = 2, map = 40, blob = 54133, race = 1, points = {} }, 
		[L["Dun Garok Digsite"]] = { continent = 2, map = 24, blob = 54134, race = 1, points = {} }, 
		[L["Southshore Fossil Field"]] = { continent = 2, map = 24, blob = 54135, race = 3, points = {} }, 
		[L["Aerie Peak Digsite"]] = { continent = 2, map = 26, blob = 54136, race = 1, points = {} }, 
		[L["Shadra'Alor Digsite"]] = { continent = 2, map = 26, blob = 54137, race = 8, points = {} }, 
		[L["Altar of Zul Digsite"]] = { continent = 2, map = 26, blob = 54138, race = 8, points = {} }, 
		[L["Jintha'Alor Lower City Digsite"]] = { continent = 2, map = 26, blob = 54139, race = 8, points = {} }, 
		[L["Jintha'Alor Upper City Digsite"]] = { continent = 2, map = 26, blob = 54140, race = 8, points = {} }, 
		[L["Agol'watha Digsite"]] = { continent = 2, map = 26, blob = 54141, race = 8, points = {} }, 
		[L["Hammertoe's Digsite"]] = { continent = 2, map = 17, blob = 54832, race = 1, points = {} }, 
		[L["Tomb of the Watchers Digsite"]] = { continent = 2, map = 17, blob = 54834, race = 1, points = {} }, 
		[L["Uldaman Entrance Digsite"]] = { continent = 2, map = 17, blob = 54838, race = 1, points = {} }, 
		[L["Sunken Temple Digsite"]] = { continent = 2, map = 38, blob = 54862, race = 8, points = {} }, 
		[L["Misty Reed Fossil Bank"]] = { continent = 2, map = 38, blob = 54864, race = 3, points = {} }, 
		[L["Twilight Grove Digsite"]] = { continent = 2, map = 34, blob = 55350, race = 4, points = {} }, 
		[L["Vul'Gol Fossil Bank"]] = { continent = 2, map = 34, blob = 55352, race = 3, points = {} }, 
		[L["Nazj'vel Digsite"]] = { continent = 1, map = 42, blob = 55354, race = 4, points = {} }, 
		[L["Zoram Strand Digsite"]] = { continent = 1, map = 43, blob = 55356, race = 4, points = {} }, 
		[L["Ruins of Ordil'Aran"]] = { continent = 1, map = 43, blob = 55398, race = 4, points = {} }, 
		[L["Ruins of Stardust"]] = { continent = 1, map = 43, blob = 55400, race = 4, points = {} }, 
		[L["Forest Song Digsite"]] = { continent = 1, map = 43, blob = 55402, race = 4, points = {} }, 
		[L["Stonetalon Peak"]] = { continent = 1, map = 81, blob = 55404, race = 4, points = {} }, 
		[L["Ruins of Eldre'Thar"]] = { continent = 1, map = 81, blob = 55406, race = 4, points = {} }, 
		[L["Unearthed Grounds"]] = { continent = 1, map = 81, blob = 55408, race = 3, points = {} }, 
		[L["Bael Modan Digsite"]] = { continent = 1, map = 607, blob = 55410, race = 1, points = {} }, 
		[L["Ruins of Eldarath"]] = { continent = 1, map = 181, blob = 55412, race = 4, points = {} }, 
		[L["Ruins of Arkkoran"]] = { continent = 1, map = 181, blob = 55414, race = 4, points = {} }, 
		[L["Lakeridge Highway Fossil Bank"]] = { continent = 2, map = 36, blob = 55416, race = 3, points = {} }, 
		[L["Slitherblade Shore Digsite"]] = { continent = 1, map = 101, blob = 55418, race = 4, points = {} }, 
		[L["Ethel Rethor Digsite"]] = { continent = 1, map = 101, blob = 55420, race = 4, points = {} }, 
		[L["Valley of Bones"]] = { continent = 1, map = 101, blob = 55422, race = 3, points = {} }, 
		[L["Mannoroc Coven Digsite"]] = { continent = 1, map = 101, blob = 55424, race = 4, points = {} }, 
		[L["Kodo Graveyard"]] = { continent = 1, map = 101, blob = 55426, race = 3, points = {} }, 
		[L["Sargeron Digsite"]] = { continent = 1, map = 101, blob = 55428, race = 4, points = {} }, 
		[L["Red Reaches Fossil Bank"]] = { continent = 2, map = 19, blob = 55434, race = 3, points = {} }, 
		[L["Dreadmaul Fossil Field"]] = { continent = 2, map = 19, blob = 55436, race = 3, points = {} }, 
		[L["Grimsilt Digsite"]] = { continent = 2, map = 28, blob = 55438, race = 1, points = {} }, 
		[L["Pyrox Flats Digsite"]] = { continent = 2, map = 28, blob = 55440, race = 1, points = {} }, 
		[L["Western Ruins of Thaurissan"]] = { continent = 2, map = 29, blob = 55442, race = 1, points = {} }, 
		[L["Eastern Ruins of Thaurissan"]] = { continent = 2, map = 29, blob = 55444, race = 1, points = {} }, 
		[L["Terror Wing Fossil Field"]] = { continent = 2, map = 29, blob = 55446, race = 3, points = {} }, 
		[L["Zul'Mashar Digsite"]] = { continent = 2, map = 23, blob = 55448, race = 8, points = {} }, 
		[L["Quel'Lithien Lodge Digsite"]] = { continent = 2, map = 23, blob = 55450, race = 4, points = {} }, 
		[L["Infectis Scar Fossil Field"]] = { continent = 2, map = 23, blob = 55452, race = 3, points = {} }, 
		[L["Eastern Zul'Kunda Digsite"]] = { continent = 2, map = 37, blob = 55454, race = 8, points = {} }, 
		[L["Western Zul'Kunda Digsite"]] = { continent = 2, map = 37, blob = 55456, race = 8, points = {} }, 
		[L["Bal'lal Ruins Digsite"]] = { continent = 2, map = 37, blob = 55458, race = 8, points = {} }, 
		[L["Balia'mah Digsite"]] = { continent = 2, map = 37, blob = 55460, race = 8, points = {} }, 
		[L["Ziata'jai Digsite"]] = { continent = 2, map = 37, blob = 55462, race = 8, points = {} }, 
		[L["Eastern Zul'Mamwe Digsite"]] = { continent = 2, map = 37, blob = 55464, race = 8, points = {} }, 
		[L["Western Zul'Mamwe Digsite"]] = { continent = 2, map = 37, blob = 55466, race = 8, points = {} }, 
		[L["Savage Coast Raptor Fields"]] = { continent = 2, map = 37, blob = 55468, race = 3, points = {} }, 
		[L["Ruins of Aboraz"]] = { continent = 2, map = 673, blob = 55470, race = 8, points = {} }, 
		[L["Ruins of Jubuwal"]] = { continent = 2, map = 673, blob = 55472, race = 8, points = {} }, 
		[L["Gurubashi Arena Digsite"]] = { continent = 2, map = 673, blob = 55474, race = 8, points = {} }, 
		[L["Nek'mani Wellspring Digsite"]] = { continent = 2, map = 673, blob = 55476, race = 8, points = {} }, 
		[L["Felstone Fossil Field"]] = { continent = 2, map = 22, blob = 55478, race = 3, points = {} }, 
		[L["Northridge Fossil Filed"]] = { continent = 2, map = 22, blob = 55480, race = 3, points = {} }, 
		[L["Andorhal Fossil Bank"]] = { continent = 2, map = 22, blob = 55482, race = 3, points = {} }, 
		[L["Wyrmbog Fossil Field"]] = { continent = 1, map = 141, blob = 55755, race = 3, points = {} }, 
		[L["Quagmire Fossil Field"]] = { continent = 1, map = 141, blob = 55757, race = 3, points = {} }, 
		[L["Dire Maul Digsite"]] = { continent = 1, map = 121, blob = 56327, race = 4, points = {} }, 
		[L["Broken Commons Digsite"]] = { continent = 1, map = 121, blob = 56329, race = 4, points = {} }, 
		[L["Ravenwind Digsite"]] = { continent = 1, map = 121, blob = 56331, race = 4, points = {} }, 
		[L["Oneiros Digsite"]] = { continent = 1, map = 121, blob = 56333, race = 4, points = {} }, 
		[L["Solarsal Digsite"]] = { continent = 1, map = 121, blob = 56335, race = 4, points = {} }, 
		[L["Darkmist Digsite"]] = { continent = 1, map = 121, blob = 56337, race = 4, points = {} }, 
		[L["South Isildien Digsite"]] = { continent = 1, map = 121, blob = 56339, race = 4, points = {} }, 
		[L["North Isildien Digsite"]] = { continent = 1, map = 121, blob = 56341, race = 4, points = {} }, 
		[L["Constellas Digsite"]] = { continent = 1, map = 182, blob = 56343, race = 4, points = {} }, 
		[L["Morlos'Aran Digsite"]] = { continent = 1, map = 182, blob = 56345, race = 4, points = {} }, 
		[L["Jaedenar Digsite"]] = { continent = 1, map = 182, blob = 56347, race = 4, points = {} }, 
		[L["Ironwood Digsite"]] = { continent = 1, map = 182, blob = 56349, race = 4, points = {} }, 
		[L["Lake Kel'Theril Digsite"]] = { continent = 1, map = 281, blob = 56351, race = 4, points = {} }, 
		[L["Owl Wing Thicket Digsite"]] = { continent = 1, map = 281, blob = 56354, race = 4, points = {} }, 
		[L["Frostwhisper Gorge Digsite"]] = { continent = 1, map = 281, blob = 56356, race = 4, points = {} }, 
		[L["Fields of Blood Fossil Bank"]] = { continent = 1, map = 607, blob = 56358, race = 3, points = {} }, 
		[L["Nightmare Scar Digsite"]] = { continent = 1, map = 607, blob = 56362, race = 4, points = {} }, 
		[L["Zul'Farrak Digsite"]] = { continent = 1, map = 161, blob = 56364, race = 8, points = {} }, 
		[L["Broken Pillar Digsite"]] = { continent = 1, map = 161, blob = 56367, race = 8, points = {} }, 
		[L["Eastmoon Ruins Digsite"]] = { continent = 1, map = 161, blob = 56369, race = 8, points = {} }, 
		[L["Southmoon Ruins Digsite"]] = { continent = 1, map = 161, blob = 56371, race = 8, points = {} }, 
		[L["Dunemaul Fossil Ridge"]] = { continent = 1, map = 161, blob = 56373, race = 3, points = {} }, 
		[L["Abyssal Sands Fossil Ridge"]] = { continent = 1, map = 161, blob = 56375, race = 3, points = {} }, 
		[L["Lower Lakkari Tar Pits"]] = { continent = 1, map = 201, blob = 56380, race = 3, points = {} }, 
		[L["Upper Lakkari Tar Pits"]] = { continent = 1, map = 201, blob = 56382, race = 3, points = {} }, 
		[L["Terror Run Fossil Field"]] = { continent = 1, map = 201, blob = 56384, race = 3, points = {} }, 
		[L["Screaming Reaches Fossil Field"]] = { continent = 1, map = 201, blob = 56386, race = 3, points = {} }, 
		[L["Marshlands Fossil Bank"]] = { continent = 1, map = 201, blob = 56388, race = 3, points = {} }, 
		[L["Southwind Village Digsite"]] = { continent = 1, map = 261, blob = 56390, race = 4, points = {} }, 
		[L["Gor'gaz Outpost Digsite"]] = { continent = 3, map = 465, blob = 56392, race = 6, points = {} }, 
		[L["Zeth'Gor Digsite"]] = { continent = 3, map = 465, blob = 56394, race = 6, points = {} }, 
		[L["Hellfire Basin Digsite"]] = { continent = 3, map = 465, blob = 56396, race = 6, points = {} }, 
		[L["Hellfire Citadel Digsite"]] = { continent = 3, map = 465, blob = 56398, race = 6, points = {} }, 
		[L["Sha'naar Digsite"]] = { continent = 3, map = 465, blob = 56400, race = 2, points = {} }, 
		[L["Boha'mu Ruins Digsite"]] = { continent = 3, map = 467, blob = 56402, race = 2, points = {} }, 
		[L["Twin Spire Ruins Digsite"]] = { continent = 3, map = 467, blob = 56404, race = 2, points = {} }, 
		[L["Ruins of Enkaat Digsite"]] = { continent = 3, map = 479, blob = 56406, race = 2, points = {} }, 
		[L["Arklon Ruins Digsite"]] = { continent = 3, map = 479, blob = 56408, race = 2, points = {} }, 
		[L["Ruins of Farahlon Digsite"]] = { continent = 3, map = 479, blob = 56410, race = 2, points = {} }, 
		[L["Ancestral Grounds Digsite"]] = { continent = 3, map = 477, blob = 56412, race = 6, points = {} }, 
		[L["Sunspring Post Digsite"]] = { continent = 3, map = 477, blob = 56416, race = 6, points = {} }, 
		[L["Laughing Skull Digsite"]] = { continent = 3, map = 477, blob = 56418, race = 6, points = {} }, 
		[L["Burning Blade Digsite"]] = { continent = 3, map = 477, blob = 56420, race = 6, points = {} }, 
		[L["Halaa Digsite"]] = { continent = 3, map = 477, blob = 56422, race = 2, points = {} }, 
		[L["Grangol'var Village Digsite"]] = { continent = 3, map = 478, blob = 56424, race = 6, points = {} }, 
		[L["Tuurem Digsite"]] = { continent = 3, map = 478, blob = 56426, race = 2, points = {} }, 
		[L["Bleeding Hollow Ruins Digsite"]] = { continent = 3, map = 478, blob = 56428, race = 6, points = {} }, 
		[L["Bonechewer Ruins Digsite"]] = { continent = 3, map = 478, blob = 56430, race = 6, points = {} }, 
		[L["Bone Wastes Digsite"]] = { continent = 3, map = 478, blob = 56432, race = 2, points = {} }, 
		[L["East Auchindoun Digsite"]] = { continent = 3, map = 478, blob = 56434, race = 2, points = {} }, 
		[L["West Auchindoun Digsite"]] = { continent = 3, map = 478, blob = 56437, race = 2, points = {} }, 
		[L["Illidari Point Digsite"]] = { continent = 3, map = 473, blob = 56439, race = 2, points = {} }, 
		[L["Coilskar Point Digsite"]] = { continent = 3, map = 473, blob = 56441, race = 2, points = {} }, 
		[L["Ruins of Baa'ri Digsite"]] = { continent = 3, map = 473, blob = 56446, race = 2, points = {} }, 
		[L["Eclipse Point Digsite"]] = { continent = 3, map = 473, blob = 56448, race = 2, points = {} }, 
		[L["Warden's Cage Digsite"]] = { continent = 3, map = 473, blob = 56450, race = 6, points = {} }, 
		[L["Dragonmaw Fortress"]] = { continent = 3, map = 473, blob = 56455, race = 6, points = {} }, 
		[L["Skorn Digsite"]] = { continent = 4, map = 491, blob = 56504, race = 9, points = {} }, 
		[L["Halgrind Digsite"]] = { continent = 4, map = 491, blob = 56506, race = 9, points = {} }, 
		[L["Wyrmskull Digsite"]] = { continent = 4, map = 491, blob = 56508, race = 9, points = {} }, 
		[L["Shield Hill Digsite"]] = { continent = 4, map = 491, blob = 56510, race = 9, points = {} }, 
		[L["Baleheim Digsite"]] = { continent = 4, map = 491, blob = 56512, race = 9, points = {} }, 
		[L["Nifflevar Digsite"]] = { continent = 4, map = 491, blob = 56514, race = 9, points = {} }, 
		[L["Gjalerbron Digsite"]] = { continent = 4, map = 491, blob = 56516, race = 9, points = {} }, 
		[L["Pit of Narjun Digsite"]] = { continent = 4, map = 488, blob = 56518, race = 5, points = {} }, 
		[L["Moonrest Gardens Digsite"]] = { continent = 4, map = 488, blob = 56520, race = 4, points = {} }, 
		[L["En'kilah Digsite"]] = { continent = 4, map = 486, blob = 56522, race = 5, points = {} }, 
		[L["Kolramas Digsite"]] = { continent = 4, map = 496, blob = 56524, race = 5, points = {} }, 
		[L["Riplash Ruins Digsite"]] = { continent = 4, map = 486, blob = 56526, race = 4, points = {} }, 
		[L["Violet Stand Digsite"]] = { continent = 4, map = 510, blob = 56528, race = 4, points = {} }, 
		[L["Ruins of Shandaral Digsite"]] = { continent = 4, map = 510, blob = 56530, race = 4, points = {} }, 
		[L["Altar of Sseratus Digsite"]] = { continent = 4, map = 496, blob = 56533, race = 8, points = {} }, 
		[L["Zim'Rhuk Digsite"]] = { continent = 4, map = 496, blob = 56535, race = 8, points = {} }, 
		[L["Zol'Heb Digsite"]] = { continent = 4, map = 496, blob = 56537, race = 8, points = {} }, 
		[L["Altar of Quetz'lun Digsite"]] = { continent = 4, map = 496, blob = 56539, race = 8, points = {} }, 
		[L["Talramas Digsite"]] = { continent = 4, map = 486, blob = 56541, race = 5, points = {} }, 
		[L["Voldrune Digsite"]] = { continent = 4, map = 490, blob = 56543, race = 9, points = {} }, 
		[L["Drakil'Jin Ruins Digsite"]] = { continent = 4, map = 490, blob = 56547, race = 8, points = {} }, 
		[L["Brunnhildar Village Digsite"]] = { continent = 4, map = 495, blob = 56549, race = 9, points = {} }, 
		[L["Sifreldar Village Digsite"]] = { continent = 4, map = 495, blob = 56551, race = 9, points = {} }, 
		[L["Valkyrion Digsite"]] = { continent = 4, map = 495, blob = 56553, race = 9, points = {} }, 
		[L["Scourgeholme Digsite"]] = { continent = 4, map = 492, blob = 56555, race = 5, points = {} }, 
		[L["Ymirheim Digsite"]] = { continent = 4, map = 492, blob = 56560, race = 9, points = {} }, 
		[L["Jotunheim Digsite"]] = { continent = 4, map = 492, blob = 56562, race = 9, points = {} }, 
		[L["Njorndar Village Digsite"]] = { continent = 4, map = 492, blob = 56564, race = 9, points = {} }, 
		[L["Ruins of Lar'donir Digsite"]] = { continent = 1, map = 606, blob = 56566, race = 4, points = {} }, 
		[L["Shrine of Goldrinn Digsite"]] = { continent = 1, map = 606, blob = 56568, race = 4, points = {} }, 
		[L["Grove of Aessina Digsite"]] = { continent = 1, map = 606, blob = 56570, race = 4, points = {} }, 
		[L["Sanctuary of Malorne Digsite"]] = { continent = 1, map = 606, blob = 56572, race = 4, points = {} }, 
		[L["Scorched Plain Digsite"]] = { continent = 1, map = 606, blob = 56574, race = 4, points = {} }, 
		[L["Quel'Dormir Gardens Digsite"]] = { continent = 2, map = 615, blob = 56576, race = 4, points = {} }, 
		[L["Nar'shola (Middle Tier) Digsite"]] = { continent = 2, map = 615, blob = 56578, race = 4, points = {} }, 
		[L["Biel'aran Ridge Digsite"]] = { continent = 2, map = 615, blob = 56580, race = 4, points = {} }, 
		[L["Dunwald Ruins Digsite"]] = { continent = 2, map = 700, blob = 56583, race = 1, points = {} }, 
		[L["Thundermar Ruins Digsite"]] = { continent = 2, map = 700, blob = 56585, race = 1, points = {} }, 
		[L["Humboldt Conflagration Digsite"]] = { continent = 2, map = 700, blob = 56587, race = 1, points = {} }, 
		[L["Grim Batol Digsite"]] = { continent = 2, map = 700, blob = 56589, race = 1, points = {} }, 
		[L["Khartut's Tomb Digsite"]] = { continent = 1, map = 720, blob = 56591, race = 7, points = {} }, 
		[L["Tombs of the Precursors Digsite"]] = { continent = 1, map = 720, blob = 56593, race = 7, points = {} }, 
		[L["Steps of Fate Digsite"]] = { continent = 1, map = 720, blob = 56595, race = 7, points = {} }, 
		[L["Neferset Digsite"]] = { continent = 1, map = 720, blob = 56597, race = 7, points = {} }, 
		[L["Orsis Digsite"]] = { continent = 1, map = 720, blob = 56599, race = 7, points = {} }, 
		[L["Ruins of Ammon Digsite"]] = { continent = 1, map = 720, blob = 56601, race = 7, points = {} }, 
		[L["Ruins of Khintaset Digsite"]] = { continent = 1, map = 720, blob = 56603, race = 7, points = {} }, 
		[L["Temple of Uldum Digsite"]] = { continent = 1, map = 720, blob = 56605, race = 7, points = {} }, 
		[L["Ruins of Ahmtul Digsite"]] = { continent = 1, map = 720, blob = 56607, race = 7, points = {} }, 
	}

	local function LoadSiteIDToNameLookup()
		luSiteIDName = {}
		for name, site in pairs(siteData) do
			luSiteIDName[site.blob] = name
		end
	end
	LoadSiteIDToNameLookup()
	LoadMapCZTable()
end

lib.frame = lib.frame or CreateFrame("Frame")
lib.frame:Hide() 	-- ensuring it's hidden
lib.frame:RegisterEvent("PLAYER_ENTERING_WORLD")
lib.frame:RegisterEvent("WORLD_MAP_UPDATE")
lib.frame:RegisterEvent("ZONE_CHANGED_NEW_AREA")

lib.frame:RegisterEvent("UNIT_SPELLCAST_SUCCEEDED")
lib.frame:RegisterEvent("UNIT_SPELLCAST_SENT")
lib.frame:RegisterEvent("UNIT_SPELLCAST_STOP")
lib.frame:RegisterEvent("UNIT_SPELLCAST_FAILED")
lib.frame:RegisterEvent("UNIT_SPELLCAST_INTERRUPTED")

lib.frame:RegisterEvent("CURSOR_UPDATE")
lib.frame:RegisterEvent("CHAT_MSG_LOOT")
lib.frame:RegisterEvent("LOOT_CLOSED")
lib.frame:RegisterEvent("BAG_UPDATE");
lib.frame:RegisterEvent("SKILL_LINES_CHANGED");
lib.frame:RegisterEvent("CURRENCY_DISPLAY_UPDATE");

lib.frame:RegisterEvent("ARTIFACT_UPDATE");
lib.frame:RegisterEvent("ARTIFACT_COMPLETE")
lib.frame:RegisterEvent("ARTIFACT_HISTORY_READY")
lib.frame:RegisterEvent("ARTIFACT_DIG_SITE_UPDATED")

lib.frame:SetScript("OnEvent", function(frame, event, ...) lib:OnEvent(frame, event, ...) end)
lib.frame:SetScript("OnUpdate", lib.OnUpdate)

--[[
	Local helper functions
]]

-- Called when the dig site list has changed
local function DigSitesUpdated()
	if not raceData or #raceData == 0 then return end
	for cid, zlist in pairs{GetMapContinents()} do
		local sites = lib:GetContinentSites(cid, true)
		if sites then
			playerSiteData[cid] = sites
		end
	end
--	MapChanged(true)
	lib.callbacks:Fire("DigSitesUpdated")
end

-- Load and cache race data
local function LoadTables()
	if GetNumArchaeologyRaces() == 0 then return end
	if GetNumArchaeologyRaces() == #raceData then return end
	luRaceName, raceData, luCurrencyRace, luKeystoneRace = {}, {}, {}, {}
	for raceID = 1, GetNumArchaeologyRaces() do
		local raceName, currencyID, raceTexture, raceItemID = GetArchaeologyRaceInfo(raceID)
		
		local currencyName = GetCurrencyInfo(currencyID)
		local itemName, itemLink, itemRarity, itemLevel, itemMinLevel, itemType, itemSubType, itemStackCount, itemEquipLoc, itemTexture, itemSellPrice = GetItemInfo(raceItemID)

		luRaceName[raceName] = raceID
		luCurrencyRace[currencyName] = raceName
		luKeystoneRace[raceItemID] = raceID
		raceData[raceID] = { id = raceID, name = raceName, currency = { id = currencyID, name = currencyName }, texture = raceTexture, keystone = { id = raceItemID, name = itemName, texture = itemTexture } }

	end
	if #raceData > 0 then
		--lib.callbacks:Fire("ArchDataTablesLoaded")
	end
end

-- Check the chat_msg_loot message to see if it contains the archaeology profession word
local function IsArchaeologyFragment(fragment_type)
	if luCurrencyRace[fragment_type] then return true else return false end
end

-- return the race name for the currency
local function GetRaceNameForCurrency(fragment_type)
	return luCurrencyRace[fragment_type]
end

-- return the race id for the race name
local function GetRaceIDForName(name)
	return luRaceName[name]
end

-- return the race id for the currency
local function GetRaceIDForCurrency(fragment_type)
	local raceName = GetRaceNameForCurrency(fragment_type)
	if not raceName then return end
	return GetRaceIDForName(raceName)
end

-- return the race id from the keystone id lookup table
local function GetRaceIDForKeystoneID(id)
	return luKeystoneRace[id]
end

-- deformat substitute
local function MatchFormat(msg, pattern)
	return string.match(msg, string.gsub(string.gsub(pattern, "(%%s)", "(.+)"), "(%%d)", "(.+)"))
end

-- extract the itemid from the itemlink
local function GetIDFromLink(link)
	if not link then return end

	local found, _, str = string.find(link, "^|c%x+|H(.+)|h%[.+%]")
	if not found then return end

	local _, id = strsplit(":", str)
	return tonumber(id)
end

-- return the player, itemlink and quantity of the item in the chat_msg_loot
local function ParseLootMessage(msg)
	local player = UnitName("player")
	local item, quantity = MatchFormat(msg, LOOT_ITEM_SELF_MULTIPLE)
	if item and quantity then
		return player, item, tonumber(quantity)
	end
	
	quantity = 1
	item = MatchFormat(msg, LOOT_ITEM_SELF)
	if item then
		return player, item, tonumber(quantity)
	end
	
	player, item, quantity = MatchFormat(msg, LOOT_ITEM_MULTIPLE)
	if player and item and quantity then
		return player, item, tonumber(quantity)
	end
	
	quantity = 1
	player, item = MatchFormat(msg, LOOT_ITEM)
	return player, item, tonumber(quantity)
end

-- Get the map and floor for continent, zone pairs
local function GetMapFloor(continent, zone)
	local mapID = astrolabe:GetMapID(continent, zone)
	local floors = astrolabe:GetNumFloors(mapID)
	local fl = floors == 0 and 0 or 1
	return mapID, fl
end

-- Get the zone name for the zone id
local function GetZoneName(id)
	local zones = { GetMapZones(GetCurrentMapContinent()) }
	for zoneID, zoneName in pairs(zones) do
		if zoneID == id then return zoneName end
	end
	return nil
end

-- Get the zone id for the zone name
local function GetZoneID(name)
	local zones = { GetMapZones(GetCurrentMapContinent()) }
	for zoneID, zoneName in pairs(zones) do
		if zoneName == name then return zoneID end
	end
	return nil
end

local function GetWorldTarget()
	if foundTarget then return end
	if (MinimapCluster:IsMouseOver()) then return end
	local what = tooltipLeftText1:GetText()
end

local function GetIDForNode(name)
	if not nodeData[name] then return end
	return nodeData[name].id
end

local function GetRaceIDForNode(name)
	if not nodeData[name] then return end
	return nodeData[name].raceID
end

local function FoundNode(what, raceID)
	local playerPos = lib:GetPlayerPosition()
	if not playerPos then return end
	if not what then return end
	if not raceID then return end
	lib.callbacks:Fire("ArchNodeFound", raceData[raceID], playerPos, lastSite)
end

--[[
	Event Handlers
]]
-- Check when the user casts a spell to see if it was a survey
local function SpellCastSuccess(unit, spell, _, _, spellid)
	if unit ~= "player" or spellid ~= 80451 then return end
	local playerPos = lib:GetPlayerPosition()
	lastSurveyPosition = playerPos
	if playerPos then
		lastSite = lib:GetNearestSite()
	else
		lastSite = nil
	end
	lib.callbacks:Fire("PlayerCastSurvey", lastSurveyPosition, lastSite)
end

-- Check what we have looted, fragments and keystones
local function CheckLoot(msg)
	local fragment_type, amount = MatchFormat(msg, CURRENCY_GAINED_MULTIPLE)
	if fragment_type then
		if not GetRaceNameForCurrency(fragment_type) or not GetRaceIDForCurrency(fragment_type) then 
			-- we either looted a non-archaeology currency or we are missing cached data, check!
			LoadTables()
		end 
		if IsArchaeologyFragment(fragment_type) then		-- found fragments looted
			local raceID = GetRaceIDForCurrency(fragment_type)
			lib.callbacks:Fire("ReceivedFragments", raceData[raceID], amount)
		end
	else
		local _, itemLink, amount = ParseLootMessage(msg)
		if not itemLink then return end
		local itemID = GetIDFromLink(itemLink)
		local raceID = GetRaceIDForKeystoneID(itemID)
		if raceID then
			bagRefreshWas = "ReceivedKeystone"
			lastKeystoneAmount = amount
			lastRace = raceData[raceID]
			refreshLastRaceOnBagUpdate = true
		end
	end
end

-- Called when the map has changed
local function MapChanged(force)
	if lib:ZoneHasSites(force) then
		local count, actual = 0, 0
		table.foreach(lastBlobData, function(k,v) count = count + 1 end)
		lib.callbacks:Fire("MapChanged", count)
	else
		lib.callbacks:Fire("MapChanged")
	end
end

-- Called when the artifact_complete event fires
local function ArtifactCompleted(name)
	local artifact, race = lib:GetArtifactInfo(name)
	lib.callbacks:Fire("ArtifactCompleted", artifact, race)
end

-- Called when the player gains a skill up
local function SkillLinesChanged()
	local rank, maxRank = lib:GetArchaeologyRank()
	if rank and maxRank then
		if rank ~= currRank or maxRank ~= currMaxRank then
			currRank, currMaxRank = rank, maxRank
			lib.callbacks:Fire("ArchSkillUpdated", currRank, currMaxRank)
		end
	end
end

-- Called when the players currencies have changed
local function CurrenciesUpdated()
	if not raceData or #raceData == 0 then LoadTables() end
	if GetCurrencyListSize() == 0 then return end
	for raceID, race in pairs(raceData) do
		local currencyName, currencyAmount, currencyTexture, currencyUnknown, currencyWeeklyMax, currencyTotalMax = GetCurrencyInfo(race.currency.id)
		if not currencyData[race.currency.id] then
--			print("Currency Updated: ", race.name, " for ", currencyAmount, " fragments")
			currencyData[race.currency.id] = { id = race.currency.id, name = currencyName, amount = currencyAmount }
			lib:GetCurrentArtifactInfo(race.id, true)
			lib.callbacks:Fire("ReceivedFragments", race, currencyAmount)
		else
			if currencyData[race.currency.id].amount ~= currencyAmount then
				if currencyData[race.currency.id].amount < currencyAmount then
					lib:GetCurrentArtifactInfo(race.id, true)
					lib.callbacks:Fire("ReceivedFragments", race, currencyAmount)
				else
					lib.callbacks:Fire("RaceUpdated", race)
				end
				currencyData[race.currency.id].amount = currencyAmount
			end
		end
	end
end

local function ArtifactUpdated()
	if not AllowArtifactsUpdate then AllowArtifactsUpdate = true; return; end
	if not raceData or #raceData == 0 then LoadTables() end
	RequestArtifactCompletionHistory()
	lib:GetActiveArtifacts(true)
	lib.callbacks:Fire("ArtifactsUpdated")
end

local function ArtifactHistoryReady()
	if not raceData or #raceData == 0 then LoadTables() end
	lib:GetActiveArtifacts()
	lib.callbacks:Fire("ArtifactsUpdated")
end

local function BagUpdated()
	if refreshLastRaceOnBagUpdate and lastRace then
		lib:GetCurrentArtifactInfo(lastRace.id, true)
		refreshLastRaceOnBagUpdate = false
		
		if bagRefreshWas == "ReceivedKeystone" then
			lib.callbacks:Fire("ReceivedKeystone", lastRace, lastKeystoneAmount)
			lastKeystoneAmount = nil
		else
			lib.callbacks:Fire("BagUpdated", lastRace)
		end
		bagRefreshWas = nil
	end
end

local function SpellStarted(unit, spellcast, rank, target)
	if unit ~= "player" then return end
	foundTarget = false
	if spellcast == archSpell then
		curSpell = spellcast
		prevSpell = spellcast
		local nodeID = GetIDForNode(target)
		local raceID = GetRaceIDForNode(target)
--		print("Found node for ", raceID)
		if raceID then
			FoundNode(target, raceID)
			foundTarget = true
		else
			GetWorldTarget()
		end
	else
		prevSpell, curSpell = nil, nil
	end
end

local function SpellFailed(unit)
	if unit ~= "player" then return end
	prevSpell, curSpell = nil, nil
end

local function SpellStopped(unit)
	if unit ~= "player" then return end
	if archSpell == prevSpell then
		GetWorldTarget()
	end
	
	prevSpell, curSpell = curSpell, curSpell
end

local function CursorChanged()
	if foundTarget then return end
	if (MinimapCluster:IsMouseOver()) then return end
	if archSpell == prevSpell then
		GetWorldTarget()
	end
end

local function NodeLooted()
	prevSpell, curSpell = nil, nil
	foundTarget = false
end



--[[
	**** PUBLIC API STARTS HERE ****
]]





--[[
	MAPPING related functions
]]
-- Returns true if the zone has possible dig sites in it
function lib:ZoneHasSites(force)
	local playerPos = self:GetPlayerPosition()
	if not playerPos then return end
	
	if playerPos.map ~= lastMap or playerPos.floor ~= lastFloor or force then
		lastMap = map
		lastFloor = floor
		lastBlobData = self:GetCurrentZoneSiteData()
	end
	return (lastBlobData ~= nil)
end

-- Returns the players current position
function lib:GetPlayerPosition()
	local m, f, x, y = astrolabe:GetCurrentPlayerPosition()
	if not m or not f or not x or not y then return end
	local cz = mapcz[m];
	if not cz then return end
	local c, z = unpack(cz)
	return { map = m, floor = f, continent = c , zone = z, x = x, y = y }
end

-- Returns the players last known survey position
function lib:GetLastSurveyPosition()
	return lastSurveyPosition
end


--[[
	SITE related functions
]]
-- Returns the players last known dig site this session
function lib:GetLastSite()
	return lastSite
end

-- Returns the distance to the site
function lib:DistanceToSite(name)
	local playerPos = self:GetPlayerPosition()
	if not playerPos then return end
	for _, conSites in pairs(playerSiteData) do
		for _, site in pairs(conSites) do
			if site.name == name then
				local distance, _, _ = astrolabe:ComputeDistance(playerPos.map, playerPos.floor, playerPos.x, playerPos.y, site.location.map, site.location.floor, site.location.x, site.location.y)
				return distance
			end
		end
	end
end

-- Returns the nearest dig site to the player
function lib:GetNearestSite()
	local playerPos = self:GetPlayerPosition()
	if not playerPos then 
		return lastNearestSite 		-- Cannot determine where the player is currently, so we return the cached nearest site
	end
	local sites = lib:GetContinentSites(playerPos.continent)
	local nearest, distance
	
	if not sites then return end	-- No sites on the current continent, so we return nil
	for _, site in pairs(sites) do
		local dist = self:DistanceToSite(site.name)
		if not distance or distance > dist then
			distance = dist
			nearest = site
		end
	end
	lastNearestSite = nearest
	return nearest
end

-- returns the site data for the site name
function lib:GetSiteInfo(name)
	if not siteData[name] then
		print("LibArchaeology-0.1 is missing data on \"" .. name .. "\"!")
		return 
	end
	return siteData[name]
end

-- returns the site data for the current zone
function lib:GetCurrentZoneSiteData()
	local mapSites, orig = {}, GetCurrentMapAreaID()
	SetMapToCurrentZone()
	local numEntries = ArchaeologyMapUpdateAll()
	for i=1, numEntries do
		local blobID = ArcheologyGetVisibleBlobID(i)
		local name = self:GetSiteNameForBlobID(blobID)
		if name then
			mapSites[name] = self:GetSiteInfo(name)
		end
	end
	SetMapByID(orig)
	if #mapSites == 0 then return end
	return mapSites
end

-- return the site name for blob id
function lib:GetSiteNameForBlobID(id)
	return luSiteIDName[blobID]
end

-- return the sites for the map id
function lib:GetMapSites(mapID)
	local mapSites = {}
	for name, site in pairs(siteData) do
		if site.map == mapID then
			mapSites[name] = site
		end
	end
	if #mapSites == 0 then return end
	return mapSites
end

-- returns the sites for the continent
function lib:GetContinentSites(cid, force)
	if not force then
		return playerSiteData[cid]
	else
		local sites, orig = {}, GetCurrentMapAreaID()
		SetMapZoom(cid)
		local totalPOIs = GetNumMapLandmarks()
		for index = 1,totalPOIs do
			local name, description, textureIndex, px, py = GetMapLandmarkInfo(index)
			if textureIndex == SITE_TEXTURE_INDEX then
				local zoneName, fileName, texPctX, texPctY, texX, texY, scrollX, scrollY = UpdateMapHighlight(px, py)
		
				local site = self:GetSiteInfo(name)
				if site then
					local zoneID = GetZoneID(zoneName)
					local mc, fc = GetMapFloor(cid, 0)
					local mz, fz = GetMapFloor(cid, zoneID)
					local x, y = astrolabe:TranslateWorldMapPosition(mc, fc, px, py, mz, fz)
					
					if not raceData[site.race] then LoadTables() end
					local race = raceData[site.race]
					
					local digsite = {
						continent = site.continent,
						zone = { id = zoneID, name = zoneName },
						location = { map = mz, floor = fz, x = x, y = y },
						name = name,
						race = race,
						blob = site.blob,
					}
					tinsert(sites, digsite)
				end
			end
		end
		SetMapByID(orig)
		playerSiteData[cid] = sites
		return sites
	end
end

--[[
	PROFESSION related functions
]]
-- Returns true if the player has archaeology
function lib:PlayerHasArchaeology()
	local _, _, arch = GetProfessions()
	return (arch ~= nil)
end

-- Returns the skill and max skill for the player
function lib:GetArchaeologyRank()
	local _, _, arch = GetProfessions()
	if arch then
		local _, _, rank, maxRank = GetProfessionInfo(arch)
		return rank, maxRank
	else
		return nil, nil
	end
end

--[[
	RACE related functions
]]
-- Determines if a raceID is valid
function lib:IsRaceValid(raceID)
	local numRaces = GetNumArchaeologyRaces()
	return not (numRaces == 0 or not raceID or raceID > numRaces or raceID < 1)
end

-- Returns the race data for the race
function lib:GetRaceData(raceID)
	if not lib:PlayerHasArchaeology() then return end
	if not lib:IsRaceValid(raceID) then return end
	if not raceData[raceID] then LoadTables() end
	return raceData[raceID]
end

-- Returns the race IDs for a given continent
function lib:GetRaceIDsForContinent(c)
	local races = {}
	for _, site in pairs(siteData) do
		if site.continent == c then
			tinsert(races, site.race)
		end
	end
	if #races == 0 then return nil end
	return races
end

--[[
	ARTIFACT related functions
]]
-- Returns the players artifact data
function lib:GetActiveArtifacts(force)
	if GetNumArchaeologyRaces() == 0 then return end
	local artifacts = {}
	for raceID = 1, GetNumArchaeologyRaces() do
		local artifact, race = self:GetCurrentArtifactInfo(raceID, force, false)
		if artifact then
			artifact.race = race
			artifacts[raceID] = artifact
		end
	end
	if #artifacts == 0 then return nil end
	return artifacts
end

-- Returns the artifact information for a given artifact name (and race id if given)
function lib:GetArtifactInfo(name, raceID)
	if not lib:PlayerHasArchaeology() then return end
	local numRaces = GetNumArchaeologyRaces()
	local startRace = 1
	if lib:IsRaceValid(raceID) then startRace = raceID; numRaces = raceID; end
	for currRaceID = startRace, numRaces do
		local numArtifacts = GetNumArtifactsByRace(currRaceID)
		for currArtifactID = 1, numArtifacts do
			local artifactName, description, rarity, icon, spellDescription, numSockets, bgTexture, firstCompletionTime, completionCount = GetArtifactInfoByRace(currRaceID, currArtifactID);
			if name == artifactName then
				if not raceData[currRaceID] then LoadTables() end
				local race = raceData[currRaceID]
				local artifact = {
					id = currArtifactID, 
					name = artifactName,
					description = description,
					rarity = rarity,
					icon = icon,
					spellDescription = spellDescription,
					sockets = numSockets,
					backgroundTexture = bgTexture, 
					firstCompletionTime = fristCompletionTime,
					completionCount = completionCount,
				}
				return artifact, race
			end
		end
	end
end

function lib:GetArtifactCompletionInfo(raceID, artifactIdentifier)
	if not artifactIdentifier or not raceID then return end
	local artifactID, artifactName
	if type(artifactIdentifier) == "number" and artifactIdentifier ~= 0 then 
		artifactID = artifactIdentifier 
	elseif type(artifactIdentifier) == "string" and artifactIdentifier ~= "" then
		artifactName = artifactIdentifier
	else
		return
	end

	local completed, firstCompleted
	if artifactID then
		local name, _, _, _, _, _, _, firstCompletionTime, completionCount = GetArtifactInfoByRace(raceID, artifactID)
		artifactName = name
		completed = completionCount
		firstCompleted = firstCompletionTime
	else
		local numArtifacts = GetNumArtifactsByRace(raceID)
		for aid = 1, numArtifacts do
			local name, _, _, _, _, _, _, firstCompletionTime, completionCount = GetArtifactInfoByRace(raceID, aid);
			if name == artifactName then
				artifactID = aid
				completed = completionCount
				firstCompleted = firstCompeletionTime
				break
			end
		end
	end
	return artifactID, completed, firstCompleted
end

-- Returns the current artifact information for a given race
function lib:GetCurrentArtifactInfo(raceID, force, includeStones)
	if not lib:PlayerHasArchaeology() then return end
	if not lib:IsRaceValid(raceID) then return end
	
	local artifact
	if not playerArtifactData[raceID] or force then
		if not raceData[raceID] then LoadTables() end
		local race = raceData[raceID]
		local numProjects = GetNumArtifactsByRace(raceID)
		if not numProjects or numProjects == 0 then return end
		
		local name, description, rarity, icon, spellDescription, numSockets, bgTexture = GetActiveArtifactByRace(raceID)
		
		-- Get the artifacts id
		local artifactID, completed, firstCompleted
		if playerArtifactData[raceID] then
			artifactID, completed, firstCompleted = playerArtifactData[raceID].id, playerArtifactData[raceID].completionCount, playerArtifactData[raceID].firstCompletionTime
		end
		if not artifactID or not completed or not firstCompleted then
			artifactID, completed, firstCompleted = self:GetArtifactCompletionInfo(raceID, artifactID or name)
		end
		
		SetSelectedArtifact(raceID)
		local base, adjust, totalCost = GetArtifactProgress()

		numSockets = numSockets or 0
		local keystones = 0
		if race.keystone and race.keystone.id > 0 then
			keystones = GetItemCount(race.keystone.id)
		end
		
		if includeStones then
			if numSockets and numSockets > 0 then
				for socketNum = 1,numSockets do
					if not ItemAddedToArtifact(race.keystone.id) then
						SocketItemToArtifact()
					end
				end
			end
			base, adjust, totalCost = GetArtifactProgress()
		end
		
		artifact = {
			id = artifactID,
			name = name,
			description = description,
			rarity = rarity,
			icon = icon,
			spellDescription = spellDescription,
			sockets = numSockets,
			backgroundTexture = bgTexture, 
			fragments = base,
			totalFragments = totalCost,
			keystonesAvailable = keystones,
			keystoneAdjustment = adjust,
			completionCount = completed,
			firstCompletionTime = firstCompleted
		}
		playerArtifactData[raceID] = artifact			
	else
		-- update the key stones
		playerArtifactData[raceID].keystonesAvailable = GetItemCount(raceData[raceID].keystone.id)
		-- update the artifact id, completion count and first completion time if it exists
		local artifactID, completed, firstCompleted = self:GetArtifactCompletionInfo(raceID, playerArtifactData[raceID].id or playerArtifactData[raceID].name)
		playerArtifactData[raceID].id = artifactID
		playerArtifactData[raceID].completionCount = completed
		playerArtifactData[raceID].firstCompletionTime = firstCompleted
		
		artifact = playerArtifactData[raceID]
	end
	return artifact, raceData[raceID]
end

-- Checks if an artifact is solvable
function lib:CanSolveArtifact(raceID, useStones)
	if not self:PlayerHasArchaeology() then return end
	if not self:IsRaceValid(raceID) then return end
	if not raceData[raceID] then LoadTables() end
	local race = raceData[raceID]
	local numProjects = GetNumArtifactsByRace(raceID)
	if not numProjects or numProjects == 0 then 
		artifactsChecked = nil
		return
	end
	
	local name, description, rarity, icon, spellDescription, numSockets, bgTexture = GetActiveArtifactByRace(raceID)
	artifactChecked = { name = name, rarity = rarity, raceID = raceID, raceName = raceName }
	SetSelectedArtifact(raceID)
	local base, adjust, totalCost = GetArtifactProgress()
	
	if numSockets and numSockets > 0 then
		for socketNum = 1, numSockets do
			if useStones and not ItemAddedToArtifact(race.keystone.id) then
				SocketItemToArtifact()
			elseif not useStones and ItemAddedToArtifact(race.keystone.id) then
				RemoveItemFromArtifact()
			end
		end
	end
	
	local base, adjust, total = GetArtifactProgress()
	return CanSolveArtifact()
end

-- Attempts to solve an artifact
function lib:SolveArtifact(raceID, useStones)
	if not self:PlayerHasArchaeology() then return end
	if not self:IsRaceValid(raceID) then return end
	if not raceData[raceID] then LoadTables() end
	local race = raceData[raceID]
	local numProjects = GetNumArtifactsByRace(raceID)
	if not numProjects or numProjects == 0 then artifactsChecked = nil; return;	end
	
	local name, description, rarity, icon, spellDescription, numSockets, bgTexture = GetActiveArtifactByRace(raceID)
	SetSelectedArtifact(raceID)
	local base, adjust, totalCost = GetArtifactProgress()
	
	if numSockets and numSockets > 0 then
		for socketNum = 1, numSockets do
			if useStones and not ItemAddedToArtifact(race.keystone.id) then
				AllowArtifactsUpdate = false
				SocketItemToArtifact()
			elseif not useStones and ItemAddedToArtifact(race.keystone.id) then
				AllowArtifactsUpdate = false
				RemoveItemFromArtifact()
			end
		end
	end
	
	local base, adjust, total = GetArtifactProgress()
	if not CanSolveArtifact() then return end
	SolveArtifact()
	artifactSolved = { name = name, rarity = rarity, raceID = raceID, raceName = raceName }
end

-- Calculates the amount of fragments you gain if you use keystones with the artifact
function lib:ComputeSocketAdjustment(numSockets, keystones)
	local adjust = 0
	if numSockets > 0 then
		if keystones >= numSockets then
			adjust = numSockets * KEYSTONE_TO_FRAGMENTS
		else
			adjust = keystones * KEYSTONE_TO_FRAGMENTS
		end
	end
	return adjust
end



--[[
	Event Handler
]]
function lib:OnEvent(frame, event, ...)
--	print("LibArchaeology Event Handler: ", event)
	if event == "PLAYER_ENTERING_WORLD" then
		LoadTables()
		DigSitesUpdated()
		MapChanged()
		RequestArtifactCompletionHistory()
	elseif event == "WORLD_MAP_UPDATE" then
		LoadTables()
	elseif event == "ZONE_CHANGED_NEW_AREA" then
		MapChanged()
		DigSitesUpdated()
	elseif event == "ARTIFACT_DIG_SITE_UPDATED" then
		DigSitesUpdated()
	elseif event == "UNIT_SPELLCAST_SUCCEEDED" then
		SpellCastSuccess(...)
	elseif event == "UNIT_SPELLCAST_SENT" then
		SpellStarted(...)
	elseif event == "UNIT_SPELLCAST_STOP" then
		SpellStopped(...)
	elseif event == "UNIT_SPELLCAST_FAILED" or event == "UNIT_SPELLCAST_INTERRUPTED" then
		SpellFailed(...)
	elseif event == "CURSOR_UPDATE" then
		CursorChanged()
	elseif event == "LOOT_CLOSED" then
		NodeLooted()
	elseif event == "CHAT_MSG_LOOT" then
		CheckLoot(...)
	elseif event == "BAG_UPDATE" then
		BagUpdated()
	elseif event == "SKILL_LINES_CHANGED" then
		SkillLinesChanged()
	elseif event == "CURRENCY_DISPLAY_UPDATE" then
		CurrenciesUpdated()
	elseif event == "ARTIFACT_UPDATE" then
		if ArchaeologyFrame and ArchaeologyFrame:IsVisible() then return end
		ArtifactUpdated()
	elseif event == "ARTIFACT_COMPLETE" then
		local name = ...
		ArtifactCompleted(name)
	elseif event == "ARTIFACT_HISTORY_READY" then
		ArtifactHistoryReady()
	end
end

--[[
	Timer
]]
function lib:OnUpdate(elapsed)
end



--[[
Messages:
ArtifactCompleted(artifact, race)				Fired when an artifact complete event happens
ArchDataTablesLoaded							Fired when the lookup tables are loaded/updated
PlayerCastSurvey(lastSurveyPosition, lastSite)	Fired when the player casts the Survey spell
ReceivedFragments(race, amount)					Fired when the player receives fragments
ReceivedKeystone(race, amount)					Fired when the player receives key stones
DigSitesUpdated									Fired when the dig sites have been updated
MapChanged(count)								Fired when the player enters a new zone
ArchSkillUpdated(rank, maxRank)					Fired when the player gains a skill in archaeology or upgrades his archaeology
RaceUpdated(race)								Fired when an race has been modified
ArtifactsUpdated								Fired when one or more artifacts have been updated
BagUpdated(race)								Fired when an item in your bags has been updated after an archaeology event for a race
ArchNodeFound(race, position)					Fired when an archaeology node has been discovered
]]